

			《 进程 》


要如何管理进程呢？：先描述，在组织
操作系统中(进程可以同时存在非常多)

描述进程 - PCB
·进程信息被放在一个叫做 进程控制块 的数据结构中，可以理解为进程属性的集合。
·PCB : [process control block] , Linux操作系统下的 PCB是: task struct

进程 = PCB + 自己的 代码 和 数据

 
一个进程，一定要有一个PCB

【 CPU 】 <==> 【 内存 】 <==> 【磁盘[.exe可执行程序]】
     [PCB] --> [内存中 .exe的数据]
	           [内存中只是进程对应的代码和数据!]

————————————————————————————————————————————————————————————————————

struct PCB 
{
	// 所有属性			// 描述 .exe的所有属性
	// ...
	struct PCB* next;	// 用链表把各种 PCB 串起来
	// 内存指针
};

struct PCB
{
	// 所有属性			
	// ...
	struct PCB* next;	// 用链表把各种 PCB 串起来
	// 内存指针
};

————————————————————————————————————————————————————————————————————


 单纯的  .exe的代码不是进程!
 进程 = PCB + 自己的 代码 和 数据


————————————————————————————————————————————————————————————————————

    《 ldd 查看可执行程序的所用 库 》
用于列出可执行文件或共享库所依赖的共享库。

[c@192 jc]$ ldd test.exe
	linux-vdso.so.1 =>  (0x00007ffcbfbb2000)
	libc.so.6 => /lib64/libc.so.6 (0x00007fbf95684000)      // lib 里的 c 库
	/lib64/ld-linux-x86-64.so.2 (0x00007fbf95a52000)

————————————————————————————————————————————————————————————————————

		< ps >

ps 查看当前系统当中所对应的进程
ps axj	// 显示进程的详细信息（ajx 顺序可以乱）


 如何查看正在运行的指定进程
 例如：死循环程序

#include <stdio.h>
#include <unistd.h>

int main()
{
    while (1)
    {
        printf("I am a process!\n");
        sleep(1);
    }

    return 0;
}


 默认生成 ./a.out 运行； 
 死循环打印：
 I am a process!
 I am a process!
 ...

 New Window 在新窗口 执行以下代码：ps ajx | grep . / a.out
[a@192 ~]$ ps ajx | grep . / a.out
2454   3017   3017   2454 pts / 0      3017 S + 1000   0:00 . / a.out
3030   3076   3075   3030 pts / 1      3075 R + 1000   0 : 00 grep --color = auto . / a.out

 为什么是两个进程 
一个是 ./a.out 的进程，
一个是 grep 查找 ./a.out 的进程


————————————————————————————————————————————————————————————————————

		《 && 同时执行多条命令 》
[a@192 ~]$ ls&& touch log.txt

 同时拿到头部信息 和 查找的指定信息（对比：里面的数据分别代表什么？） 
[a@192 a]$ ps axj | head -1 && ps ajx | grep ./a.out
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
2454   3336   3336   2454 pts / 0      3336 S + 1000   0:00 ./a.out
3340   3525   3524   3340 pts / 1      3524 R + 1000   0 : 00 grep --color = auto ./a.out

 PID  ：每一个进程都要有自己唯一标识符，叫做进程 PID ：类型是无符号整型 unsingned int pid;


 编译程序，并吧结果重命名 my.exe 
 [a@192 a]$ gcc - o my.exe test.c      // | head -3 显示头部 3条信息，查倒数几行[c@192 ~]$ ll | tail -3
[a@192 ~]$ ps axj | head -1 && ps ajx | grep ./my.exe
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
2454   3658   3658   2454 pts / 0      3658 S + 1000   0:00 ./my.exe
3662   3735   3734   3662 pts / 2      3734 R + 1000   0 : 00 grep --color = auto ./my.exe


————————————————————————————————————————————————————————————————————

		《 查看 pid 手册：man getpid 》
 man getpid  
NAME
getpid, getppid - get process identification // 获得一个进程的标识符 pid 

SYNOPSIS	//头文件
#include <sys/types.h>
#include <unistd.h>

pid_t getpid(void);		// 无参，返回类型就是无符号整型
pid_t getppid(void);
...

————————————————————————————————————————————————————————————————————


————————————————————————————————————————————————————————————————————


			《 复制文件到指定路径 》
（1）.【cp】 + 【 -r】 + 【目录名（可带路径）】 + 【目标路径（带路径）】 - r 表示复制目录下所有子目录以及文件
（2）.【cp】 + 【目录名/*】+【目标路径（带路径）】 /*表示目录下所有文件和目录 *是通配符
 跨服务器拷贝
 scp [参数] [原路径] [目标路径]

 复制上层路径的文件到当前路径
[a@192 b]$ cp ../test.c ./

————————————————————————————————————————————————————————————————————


————————————————————————————————————————————————————————————————————


		《 Makefile 》

bin = test
src = test.c

# test:test.c //# 注释 @ ==> test == bin , ^ ==> test.c == src
$(bin) : $(src)
@gcc - o $@ $ ^
.PHONY:clean
clean :
@rm - f $(bin)


      《 test.c 》

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    pid_t id = getpid();
    while (1)
    {
        printf("I am a process! PID: %d\n", id);
        sleep(1);
    }

    return 0;
}

生成可执行程序 test 
-rw - rw - r--. 1 a a  150 Mar 24 05:35 Makefile
- rwxrwxr - x. 1 a a 8504 Mar 24 05 : 36 test
- rw - rw - r--. 1 a a  204 Mar 24 05 : 36 test.c

运行程序 ./test 
[a@192 b]$ . / test
I am a process!PID : 5495
I am a process!PID : 5495
I am a process!PID : 5495
 ......

查看自己的 PID 以及证明： 子进程
[a@192 b]$ ps axj | head -1 && ps axj | grep test
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
5186   5495   5495   5186 pts / 0      5495 S + 1000   0:00 . / test
5499   5643   5642   5499 pts / 1      5642 R + 1000   0 : 00 grep --color = auto test


		《 结束进程  ctrl + C / kill -9 pid 》
d.ctrl + C就是在用户层面终止进程，
kill -9 pid 可以用来直接杀掉进程

[a@192 ~]$ kill - 9 5722
Killed
[a@192 b]$



————————————————————————————————————————————————————————————————————


————————————————————————————————————————————————————————————————————


		《 父进程 》
// 脚本语法： 每一个一秒钟 周期性的执行 中间的命令：
while :; do  ps ajx | head -1 && ps axj | grep test; sleep 1; done


 [PPID]：父进程      [PID]：子进程

 每一次启动，子进程都有所变化，但是父进程不会改变。
[a@192 b]$ while :; do ps ajx | head -1 && ps axj | grep test; sleep 1; done
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
2536   2794   2793   2536 pts / 0      2793 R + 1000   0:00 grep --color = auto test
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
2536   2799   2798   2536 pts / 0      2798 R + 1000   0 : 00 grep --color = auto test
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
2536   2804   2803   2536 pts / 0      2803 R + 1000   0 : 00 grep --color = auto test


		< bash >  父进程，命令行解释器
 父进程的 2536 是什么？
[a@192 b]$ ps ajx | head -1 && ps axj | grep 2536
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
2528   2536   2536   2536 pts / 0      2975 Ss    1000   0:00 bash
2536   2975   2975   2536 pts / 0      2975 R + 1000   0 : 00 ps axj
2536   2976   2975   2536 pts / 0      2975 R + 1000   0 : 00 grep --color = auto 2536

————————————————————————————————————————————————————————————————————


 系统调用：《 fork [叉；分岔] 》

[a@192 b]$ man fork

NAME - 作用：创建一个子进程
fork - create a child process

SYNOPSIS
#include <unistd.h>

pid_t fork(void);
如果 fork 成功了，会返回子进程的 PID 给父进程 and 0
失败返回 ： -1  ; 生成错误码：errno


————————————————————————————————————————————————————————————————————

#include <stdio.h>

int main()
{
	printf("process is running, only me!， pid: %d\n", getpid()); 	//输出一次，父进程一次
	sleep(3);
	
	// fork 之后，父子代码共享 
	fork();
	printf("hello world! pid: %d, ppid: %d\n", getpid(), getppid());//输出两次，父进程一次，子进程一次 ,对比每次 子进程和父进程
	sleep(5);

	return 0;
}

一个是 【父进程】，一个是 【子进程】
process is running, only me!, pid: 5857
hello world, pid : 【5857】, ppid : 3299
hello world, pid : 5887, ppid : 【5857】


创建一个进程，本质是系统中多个一个进程，
多个一个进程，就是多个1.内核task_struct 2.有自己的代码和数据

父进程的代码和数据是从磁盘加载来的
子进程的代码和数据 ? ? 默认情况继承父进程的代码和数据 ?


————————————————————————————————————————————————————————————————————

 去掉 带 grep 的行显示。
 grep -v grep 

————————————————————————————————————————————————————————————————————


如果 fork 成功了，会返回子进程的 PID 给父进程 and 0
失败返回 ： -1  ; 生成错误码：errno

#include <stdio.h>

int main()
{
	printf("process is running, only me!， pid: %d\n", getpid()); 	//输出一次，父进程一次
	sleep(3);
	
	// 多进程同时跑：父 子进程
	pid_t id = fork();
	if (id == -1) return 1;

	else if (id == 0)
	{
		// child
		while (1)
		{
			printf("id: %d  I am child process! pid: %d, ppid: %d\n", id, getpid(), getppid());//输出两次，父进程一次，子进程一次 ,对比每次 子进程和父进程
			sleep(1);
		}
	}
	else
	{ 
		// parent
		while (1)
		{
			printf("id: %d  I am parent process! pid: %d, ppid: %d\n", id, getpid(), getppid());//输出两次，父进程一次，子进程一次 ,对比每次 子进程和父进程
			sleep(2);
		}
	}
	return 0;
}


[a@192 b]$ . / test
process is running, only me!， pid : 6798              【[父进程的 pid 6798]】 【[子进程的 pid 6811]】
id : 6811  I am parent process!pid : 6798, ppid : 2536 【父进程获得子进程的返回 PID：6811 】
id : 0  I am child process!pid : 6811, ppid : 6798     【子进程的父进程 6798】
id : 0  I am child process!pid : 6811, ppid : 6798     【子进程返回值为 0   】
id : 6811  I am parent process!pid : 6798, ppid : 2536
id : 0  I am child process!pid : 6811, ppid : 6798
id : 0  I am child process!pid : 6811, ppid : 6798

[a@192 ~]$ while :; do ps axj | head -1 && ps axj | grep test | grep -v grep; sleep 1; done
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
2536   6798   6798   2536 pts / 0      6798 S + 1000   0:00 . / test
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
2536   6798   6798   2536 pts / 0      6798 S + 1000   0 : 00 . / test
 《 上面 sleep 3 》
 【[父进程的 pid 6798]】 【[子进程的 pid 6811]】
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
2536   6798   6798   2536 pts / 0      6798 S + 1000   0 : 00 . / test
6798   6811   6798   2536 pts / 0      6798 S + 1000   0 : 00 . / test
PPID    PID   PGID    SID TTY       TPGID STAT   UID   TIME COMMAND
2536   6798   6798   2536 pts / 0      6798 S + 1000   0 : 00 . / test
6798   6811   6798   2536 pts / 0      6798 S + 1000   0 : 00 . / test


————————————————————————————————————————————————————————————————————


————————————————————————————————————————————————————————————————————



			《 kill 》

杀掉父进程,子进程还在跑(或者：杀掉子进程，父进程继续跑) 
父子进程是同时运行的。
 fork 会有两个返回值，返回两次
 
id: 7406  I am parent process!pid : 7405, ppid : 6281
id : 0  I am child process!pid : 7406, ppid : 7405
id : 0  I am child process!pid : 7406, ppid : 7405
id : 7406  I am parent process!pid : 7405, ppid : 6281
id : 0  I am child process!pid : 7406, ppid : 7405

[a@192 ~]$ kill - 9 7405  //杀掉父进程,子进程还在跑(或者：杀掉子进程，父进程继续跑) 
Killed
[a@192 b]$ id : 0  I am child process!pid : 7406, ppid : 1
id : 0  I am child process!pid : 7406, ppid : 1
id : 0  I am child process!pid : 7406, ppid : 1

————————————————————————————————————————————————————————————————————

 为什么 fork 会有两个返回值，返回两次？
 
进程一定要做到:进程具有独立性

进程 = 内核数据结构task_struct + 代码和数据

代码是只读的!
父子各自独立，原则上数据要分开! ==> 虚拟地址空间，父子写时拷贝 

————————————————————————————————————————————————————————————————————


		代码一次创建多进程

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>

// 子进程进来了就出不去了，防止继续执行共享代码。
void RunChild()
{
	while (1)
	{
		printf("I am child process!, pid: %d, ppid:%d\n", getpid(), getppid());
		sleep(1);
	}
}

int main()
{
	const int num = 5;
	for (int i = 0; i < num; i++)
	{
		pid_t id = fork();
		if (id == 0)	// 父进程不会进入这里，子进程进入
		{
			RunChild();
		}
		sleep(1);		// 然后父进程继续循环  
	}

	while (1)
	{
		printf("I am parent process!, pid: %d, ppid:%d\n", getpid(), getppid());
		sleep(1);
	}
}

————————————————————————————————————————————————————————————————————


————————————————————————————————————————————————————————————————————


task_struct内容分类：
·标示符       : 描述本进程的唯一标示符，用来区别其他进程。状态:任务状态，退出代码，退出信号等。
·优先级       : 相对于其他进程的优先级。
·程序计数器   : 程序中即将被执行的下一条指令的地址。
·内存指针     : 包括程序代码和进程相关数据的指针，还有和其他进程共享的内存块的指针
·上下文数据   :进程执行时处理器的寄存器中的数据[休学例子，要加图CPU，寄存器]。
·I/O 状态信息 : 包括显示的 I/O 请求, 分配给进程的 I/O 设备和被进程使用的文件列表。
·记账信息     : 可能包括处理器时间总和，使用的时钟数总和，时间限制，记账号等。
·其他信息

组织进程：
可以在内核源代码里找到它。
所有运行在系统里的进程都以 task_struct 链表 的形式存在内核里。

————————————————————————————————————————————————————————————————————

			< proc >
查看进程
进程的信息可以通过 /proc 系统文件夹查看
·如 : 要获取PID为1的进程信息，你需要查看 /proc/1 这个文件夹。

首先进入根目录: ls /
dr - xr - xr - x121 root root Feb  2 12:40  proc

ls /proc/17611 -d
proc/17611		// 会形成一个以该进程命名的文件夹 ，里面保存的就是文件的属性信息
//查看有那些信息
ls /proc/17611 -l
// ...
lrwxrwxrwx 1 whb whb 0 Mar 10 12:26 exe -> / home / Desktop / a / b /test	//由这个绝对路径 加载形成的进程
// ...


//如果把可执行程序删了：  rm test
lrwxrwxrwx 1 whb whb 0 Mar 10 12 : 26 exe -> / home / Desktop / a / b / test (deleted)		//显示已经删除

————————————————————————————————————————————————————————————————————

			< cwd > 
current work dir [进程的当前工作路径]
1rwxrwxrwx 1 whb whb 0 Mar 10 12:26 cwd -> / home / Desktop / a / b		

————————————————————————————————————————————————————————————————————


————————————————————————————————————————————————————————————————————


